/*
 * Funambol is a mobile platform developed by Funambol, Inc. 
 * Copyright (C) 2008 Funambol, Inc.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission 
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE 
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Affero General Public License 
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 * 
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite 
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 * 
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 * 
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably 
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "Alarm.h"
#include "EventDataStore.h"
#include "base/Log.h"


bool AlarmDataStore::containsReminder(const char* identifier, ArrayList& al) {
        
    StringBuffer query("select rowid from Alarm where entity_id = ");
    query.append(identifier).append(";");
    bool ret = false;
    
    int result = driver.queryIndex(query, al);
    if (result != 0){
        LOG.error("AlarmDataStore.containsReminder: error queryIndex: err = %i", result);
        goto finally;        
    }
    if (al.size() > 0) 
        ret = true;

finally:
    return ret;
    
}


StringBuffer AlarmDataStore::createInsertAlarmStmt(Event& event, const char* identifier) { 
    
    Alarm* alarm = event.getAlarm();
    if (!alarm) {
        LOG.error("The havent has no alarm set properly... Abort the alarm insertion.");
        return "";
    }
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_ALARM);
    query.append(" ("); 
    // list of fields
    query.append("trigger_date, trigger_interval, type, entity_id, entity_type");
    
    StringBuffer timealarm = alarm->getPrimary_secBeforeStart(NULL);
    
    Timezone tz;
    tz.setName(getCurrentTimezone());
    Date d("19010101T000000", tz);
    // list of values
    query.append(") values ("); 
    query.append(convertStandardToAppleDate(d)).append(",");
    query.append(timealarm).append(",");
    query.append("0").append(",");
    query.append(identifier).append(",");
    query.append("2");

	LOG.error("1 Alarm inserted");

	if (alarm->getSecondary_secBeforeStart(NULL) != 0) {
	    StringBuffer timealarm2 = alarm->getSecondary_secBeforeStart(NULL);
		// list of values
		query.append("), ("); 
		query.append(convertStandardToAppleDate(d)).append(",");
		query.append(timealarm2).append(",");
		query.append("0").append(",");
		query.append(identifier).append(",");
		query.append("2");
		LOG.error("2 Alarm inserted");
	}

	query.append(");");	
	
    LOG.debug("**AlarmDataStore:createInsertAlarmStmt: %s", query.c_str());
    return query;
}

StringBuffer AlarmDataStore::createInsertAlarmChangesStmt(Event& event, const char* identifier, const char* alarmid) {
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_ALARM_CHANGES);
    query.append(" ("); 
    // list of fields
    query.append("record, type, entity_id, entity_type, external_id, store_id, calendar_id");
    
    // list of values
    query.append(") values ("); 
    query.append(alarmid).append(",");
    query.append("0").append(",");
    query.append(identifier).append(",");
    query.append("2").append(",");
    query.append("NULL").append(",");
    query.append("1").append(",");
    query.append(event.getCalendar()).append(");");
    
    LOG.debug("**AlarmDataStore:createInsertAlarmChangesStmt: %s", query.c_str());
    return query;    
}


StringBuffer AlarmDataStore::createUpdateAlarmStmt(Event& event, const char* identifier) {    
    
    Alarm* alarm = event.getAlarm();
    if (!alarm) {
        LOG.error("The havent has no alarm set properly... Abort the alarm update.");
        return "";
    }
    StringBuffer query("UPDATE ");
    query.append(TABLE_ALARM);
    query.append(" set ");    
    
    // list of values
    StringBuffer timealarm = alarm->getPrimary_secBeforeStart(NULL);
    
    query.append(" trigger_interval = ");
    query.append(timealarm).append(",");    
    
    query.append(" where event_id = ");
    query.append(identifier);    
    query.append(";");    
    
    LOG.debug("**AlarmDataStore:createInsertOccurrenceCacheStmt: %s", query.c_str()); 
    return query;          
}

StringBuffer AlarmDataStore::createUpdateAlarmChangesStmt(Event& event, const char* identifier, const char* alarmid) {
    
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_ALARM_CHANGES);
    query.append(" ("); 
    // list of fields
    query.append("record, type, entity_id, entity_type, external_id, store_id, calendar_id");
    
    // list of values
    query.append(") values ("); 
    query.append(alarmid).append(",");
    query.append("1").append(",");
    query.append(identifier).append(",");
    query.append("2").append(",");
    query.append("NULL").append(",");
    query.append("1").append(",");
    query.append(event.getCalendar()).append(");");
    
    LOG.debug("**AlarmDataStore:createUpdateAlarmChangesStmt: %s", query.c_str());
    return query;    
}

StringBuffer AlarmDataStore::createDeleteAlarmChangesStmt(Event& event, const char* identifier, const char* alarmid) {
       
    StringBuffer query("INSERT INTO ");
    query.append(TABLE_ALARM_CHANGES);
    query.append(" ("); 
    
    // list of fields
    query.append("record, type, entity_id, entity_type, external_id, store_id, calendar_id");
    
    // list of values
    query.append(") values ("); 
    query.append(alarmid).append(",");
    query.append("2").append(",");
    query.append(identifier).append(",");
    query.append("2").append(",");
    query.append("NULL").append(",");
    query.append("1").append(",");
    query.append(event.getCalendar()).append(");");
    
    LOG.debug("**AlarmDataStore:createInsertAlarmChangesStmt: %s", query.c_str());
    return query;    
}

StringBuffer AlarmDataStore::createDeleteAlarmStmt(const char* identifier) {
    
    StringBuffer query("DELETE FROM ");
    query.append(TABLE_ALARM);
    query.append(" WHERE entity_id = ");
    query.append(identifier);
    query.append(";");
    LOG.debug("**AlarmDataStore:createDeleteAlarmStmt: %s", query.c_str()); 
    return query;
    
}

int AlarmDataStore::insertAlarm(Event& event, const char* identifier) {
    
    int ret = 0;
    ArrayList al;
    StringBuffer alarmid;
    
    StringBuffer query = createInsertAlarmStmt(event, identifier);
    ret = driver.execute(query);
    if (ret != 0) {
        LOG.error("AlarmDataStore.insertAlarm: error createInsertAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    ret = driver.commit();
    if (ret != 0) {
        LOG.error("AlarmDataStore.insertAlarm: error commit: err = %i", ret);
        goto finally;
    }
    
    ret = driver.queryIndex(QUERY_MAX_ID_FROM_ALARM, al);
    if (ret != 0){
        LOG.error("AlarmDataStore.insertAlarm: error queryIndex QUERY_MAX_ID_FROM_ALARM: err = %i", ret);                
        goto finally;        
    }
    
    alarmid = ((StringBuffer*)(al.get(0)))->c_str();
    
    query = createInsertAlarmChangesStmt(event, identifier, alarmid);
    ret = driver.execute(query);
    if (ret != 0) {
        LOG.error("AlarmDataStore.insertAlarm: error createInsertAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    ret = driver.commit();
    if (ret != 0) {
        LOG.error("AlarmDataStore.insertAlarm: error commit: err = %i", ret);
        goto finally;
    }
    
finally:
    return ret;
}

/**
 * It modifies only the alarmChanges table because the Alarm table is changed according the trigger
 * in the database.
 * @param event - the event
 * @param identifer - the id of the event
 * @param alsoAlarmTable - if the alarm has to be deleted also from the Alarm table.
 *                         usually the Alarm table is handled with a trigger of the database
 *                         so there is no need to remove from it.
 * QUERY_GET_ALARM_ID = SELECT ROWID from Alarm where entity_id = identifier;
 */
int AlarmDataStore::deleteAlarm(Event& event, const char* identifier, bool alsoAlarmTable) {
    
    int ret = 0;
    ArrayList al;       
    StringBuffer query(QUERY_GET_ALARM_ID);
    query.append(identifier).append(";");
    LOG.debug("Query to delete alarmid: %s", query.c_str());
    
    ret = driver.queryIndex(query, al);
    if (ret != 0){
        LOG.error("AlarmDataStore.deleteAlarm: error commit after query get alarm id: err = %i", ret);
        goto finally;
    }
    
    for (int i = 0; i < al.size(); i++) {
        StringBuffer* alarmid = (StringBuffer*)al.get(i);
        query = createDeleteAlarmChangesStmt(event, identifier, alarmid->c_str());
        ret = driver.execute(query);
        if (ret != 0){
            LOG.error("AlarmDataStore.deleteAlarm: error createDeleteAlarmChangesStmt: err = %i", ret);
            goto finally;
        }
        
        ret = driver.commit();
        if (ret != 0){
            LOG.error("AlarmDataStore.deleteAlarm: error commit after createDeleteAlarmChangesStmt: err = %i", ret);
            goto finally;
        }
    }   
    
    if (alsoAlarmTable) {
        query = createDeleteAlarmStmt(identifier);
        ret = driver.execute(query);
        if (ret != 0){
            LOG.error("AlarmDataStore.deleteAlarm: error createDeleteAlarmChangesStmt: err = %i", ret);
            goto finally;
        }
        
        ret = driver.commit();
        if (ret != 0){
            LOG.error("AlarmDataStore.deleteAlarm: error commit after createDeleteAlarmChangesStmt: err = %i", ret);
            goto finally;
        }
    }
    
finally:
    return ret;
}

int AlarmDataStore::updateAlarm(Event& event, const char* identifier, const char* alarmid) {
    
    int ret = 0;
    ArrayList al;       
    StringBuffer query("update Alarm set trigger_interval = ");
    Alarm* alarm = event.getAlarm();
    if (!alarm) {
        LOG.error("AlarmDataStore.updateAlarm: error getting the alarm: aborted update");
        return 1;
    }
    query.append(alarm->getPrimary_secBeforeStart(NULL)).append(" ");
    query.append("where rowid = ");
    query.append(alarmid).append(";");
    LOG.debug("Query to update Alarm table is: %s", query.c_str());
    
    ret = driver.execute(query);
    if (ret != 0){
        LOG.error("AlarmDataStore.deleteAlarm: error createDeleteAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    
    ret = driver.commit();
    if (ret != 0){
        LOG.error("AlarmDataStore.deleteAlarm: error commit after createDeleteAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    
    query = createUpdateAlarmChangesStmt(event, identifier, alarmid);
    ret = driver.execute(query);
    if (ret != 0){
        LOG.error("AlarmDataStore.deleteAlarm: error createDeleteAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    
    ret = driver.commit();
    if (ret != 0){
        LOG.error("AlarmDataStore.deleteAlarm: error commit after createDeleteAlarmChangesStmt: err = %i", ret);
        goto finally;
    }
    
finally:
    return ret;
    
}


Alarm* AlarmDataStore::getAlarm(Event& event, const char* id_event) {
    
    ArrayList al;
    int ret = 0;
    Alarm* alarm = NULL;
    if (!containsReminder(id_event, al)) {
        LOG.debug("There are no Alarm for the event");
        goto finally;
    }
    
    alarm = new Alarm();
    for (int i = 0; i < al.size(); i++) {
        ArrayList result;
        StringBuffer query("select trigger_interval from Alarm where ROWID = ");
        query.append(*((StringBuffer*)al.get(i))).append(";");
        ret = driver.queryIndex(query, result);
        if (ret != 0) {
            LOG.debug("AlarmDataStore:getAlarm error getting the trigger_interval: %i", ret);
            goto finally;
        }
        if (i == 0) {
            alarm->setPrimary_secBeforeStart(((StringBuffer*)result.get(0))->c_str());
			LOG.debug("AlarmDataStore:getAlarm 1 Alarm set, %i to go", al.size());
        } else {
			alarm->setSecondary_secBeforeStart(((StringBuffer*)result.get(0))->c_str());
			LOG.debug("AlarmDataStore:getAlarm 2 Alarm set");
        }
    }
    
    alarm->completeFields(event.getStart());
	
finally:
    return alarm;
}







